package megrez.job.custom.web;

import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import megrez.job.custom.component.ScheduledTaskContainer;
import megrez.job.custom.component.ScheduledTaskManager;
import megrez.job.custom.component.Task;
import megrez.job.custom.domain.Log;
import megrez.job.custom.domain.LogRepository;
import megrez.job.custom.domain.LogSpecifications;
import megrez.job.custom.domain.TaskState;
import megrez.job.custom.web.vo.LogVO;
import megrez.job.custom.web.vo.TaskVO;

/**
 * 任务接口
 * 
 * @author Lucky Yang
 * @since 2.6.9
 */
@RestController
@RequestMapping("/v1/tasks")
@RequiredArgsConstructor
public class TaskResource {
    private final ScheduledTaskManager scheduleTaskManager;
    private final LogRepository logRep;

    @PostMapping("/{configId}/start")
    public TaskVO start(@PathVariable("configId") String configId) {
        Task task = scheduleTaskManager.start(configId);
        return TaskVO.of(task);
    }

    @PostMapping("/{configId}/stop")
    public TaskVO stop(@PathVariable("configId") String configId) {
        Task task = scheduleTaskManager.stop(configId);
        return TaskVO.of(task);
    }

    @GetMapping("/{configId}")
    public TaskVO getTask(@PathVariable("configId") String configId) {
        Task task = scheduleTaskManager.getTaskInfo(configId);
        return TaskVO.of(task);
    }

    @GetMapping()
    public List<TaskVO> getTasks(@RequestParam(value = "className", required = false) String className,
            @RequestParam(value = "enabled", required = false) Boolean enabled,
            @RequestParam(value = "states", required = false) Set<TaskState> states,
            @RequestParam(value = "asc", required = false) Boolean asc) {
        Comparator<TaskVO> lastRunTimeComp = Comparator.comparing(TaskVO::getLastRunTime,
                Comparator.nullsLast(Date::compareTo));

        Stream<TaskVO> stream = ScheduledTaskContainer.getTasks().stream().map(TaskVO::of);
        if (StringUtils.hasText(className)) {
            stream = stream.filter(e -> e.getClassName().toUpperCase().indexOf(className.toUpperCase()) >= 0);
        }
        if (enabled != null) {
            stream = stream.filter(e -> enabled.equals(e.getEnabled()));
        }
        if (!CollectionUtils.isEmpty(states)) {
            stream = stream.filter(e -> states.contains(e.getState()));
        }
        if (Boolean.FALSE.equals(asc)) {
            lastRunTimeComp = lastRunTimeComp.reversed();
        }
        return stream.sorted(lastRunTimeComp).collect(Collectors.toList());
    }

    @GetMapping("/log")
    public Page<LogVO> getLogPage(@RequestParam(value = "configId", required = false) String configId,
            @RequestParam(value = "states", required = false) Set<TaskState> states, Pageable pageable) {

        Specification<Log> where = LogSpecifications.empty();
        if (StringUtils.hasText(configId)) {
            where = where.and(LogSpecifications.configIdEqual(configId));
        }
        if (!CollectionUtils.isEmpty(states)) {
            where = where.and(LogSpecifications.stateIn(states));
        }
        Page<Log> page = logRep.findAll(where, pageable);
        return page.map(LogVO::of);
    }

    @DeleteMapping("/{configId}/log")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable("configId") String configId) {
        logRep.deleteByConfigId(configId);
    }
}
